﻿package com.ease.plugin.pluginClasses {
  import com.ease.core.plugin.ResourceBase;
  import com.ease.plugin.PluginFactory;
  import com.ease.plugin.PluginFactoryItem;
  import com.ease.plugin.URLResource;
  import com.ease.plugin.events.ErrorEvent;
  import com.ease.plugin.events.FactoryEvent;
  import com.ease.plugin.events.LoadEvent;
  import com.ease.plugin.events.PluginManagerEvent;
  import com.ease.plugin.traits.LoadState;
  import com.ease.plugin.traits.LoadTrait;
  import com.ease.plugin.traits.TraitType;

  import flash.events.EventDispatcher;
  import flash.utils.Dictionary;

  import mx.collections.ArrayCollection;

  [Event(name="pluginLoad", type="com.ease.plugin.events.PluginManagerEvent")]

  [Event(name="pluginLoadError", type="com.ease.plugin.events.PluginManagerEvent")]

  public class PluginManager extends EventDispatcher {
    public function PluginManager(mediaFactory:PluginFactory) {
      super();

      _mediaFactory = mediaFactory;
      _mediaFactory.addEventListener(FactoryEvent.PLUGIN_ELEMENT_CREATE, onMediaElementCreate);

      initPluginFactory();
      _pluginList = new ArrayCollection();
    }

    public function loadPlugin(resource:ResourceBase):void {
      if (resource == null) {
        throw new ArgumentError("");
      }

      var identifier:Object = getPluginIdentifier(resource);
      var pluginEntry:PluginEntry = _pluginMap[identifier] as PluginEntry;
      if (pluginEntry != null) {
        dispatchEvent
          ( new PluginManagerEvent
            ( PluginManagerEvent.PLUGIN_LOAD
            , false
            , false
            , resource
            )
          );
      }
      else {
        var pluginElement:PluginElement = _pluginFactory.createPluginElement(resource) as PluginElement;

        if (pluginElement != null) {
          pluginEntry = new PluginEntry(pluginElement, PluginLoadingState.LOADING);
          _pluginMap[identifier] = pluginEntry;

          var loadTrait:LoadTrait = pluginElement.getTrait(TraitType.LOAD) as LoadTrait;
          if (loadTrait != null) {
            loadTrait.addEventListener(LoadEvent.LOAD_STATE_CHANGE, onLoadStateChange);
            loadTrait.load();
          }
          else {
            dispatchEvent(new PluginManagerEvent(PluginManagerEvent.PLUGIN_LOAD_ERROR, false, false, resource));
          }
        }
        else {
          dispatchEvent(new PluginManagerEvent(PluginManagerEvent.PLUGIN_LOAD_ERROR, false, false, resource));
        }
      }

      function onLoadStateChange(event:LoadEvent):void {
        if (event.loadState == LoadState.READY) {
          pluginEntry.state = PluginLoadingState.LOADED;
          _pluginList.addItem(pluginEntry);

          var pluginLoadTrait:PluginLoadTrait = pluginElement.getTrait(TraitType.LOAD) as PluginLoadTrait;

          dispatchEvent
            ( new PluginManagerEvent
              ( PluginManagerEvent.PLUGIN_LOAD
              , false
              , false
              , resource
              )
            );
        }
        else if (event.loadState == LoadState.LOAD_ERROR) {
          delete _pluginMap[identifier];
          dispatchEvent(new PluginManagerEvent(PluginManagerEvent.PLUGIN_LOAD_ERROR, false, false, resource));
        }
      }
      function onMediaError(event:ErrorEvent):void {
        dispatchEvent(event.clone());
      }
    }

    public function get mediaFactory():PluginFactory {
      return _mediaFactory;
    }

    private function getPluginIdentifier(resource:ResourceBase):Object {
      var identifier:Object = null;

      if (resource is URLResource) {
        identifier = (resource as URLResource).url;
      }

      return identifier;
    }

    private function initPluginFactory():void {
      _pluginFactory = new PluginFactory();
      dynamicPluginLoader = new DynamicPluginLoader(mediaFactory);

      var dynamicPluginItem:PluginFactoryItem = new PluginFactoryItem
          ( DYNAMIC_PLUGIN_MEDIA_INFO_ID
          , dynamicPluginLoader.canHandleResource
          , createDynamicPluginElement
          );
      _pluginFactory.addItem(dynamicPluginItem);
    }


    private function createDynamicPluginElement():PluginElement {
      return new PluginElement(dynamicPluginLoader);
    }

    private function onMediaElementCreate(event:FactoryEvent):void {
      if (createdElements == null) {
        createdElements = new Dictionary(true);
      }
    }

    /**
     * Invokes the callback for all stored notification functions, for the given
     * MediaElement.
     **/
    private function invokeMediaElementCreationNotifications(mediaElement:PluginElement):void {
      for each (var func:Function in notificationFunctions) {
        invokeMediaElementCreationNotificationFunction(func, mediaElement);
      }
    }

    private function invokeMediaElementCreationNotificationFunction(func:Function, mediaElement:PluginElement):void {
      try {
        func.call(null, mediaElement);
      }
      catch (error:Error) {
        // Swallow, the notification function is wrongly
        // specified.  We'll continue as-is.
      }
    }

    /**
     * Invokes the creation callback on the given MediaFactoryItem, for
     * all created MediaElements.
     **/
    private function invokeMediaElementCreationNotificationForCreatedMediaElements(func:Function):void {
      // Remember, the MediaElements are stored as the keys (so
      // that they can be GC'd if the Dictionary holds the only
      // reference), hence we need to do a for..in.
      for (var elem:Object in createdElements) {
        invokeMediaElementCreationNotificationFunction(func, elem as PluginElement);
      }
    }

    private var _mediaFactory:PluginFactory;
    private var _pluginFactory:PluginFactory;
    private static var _pluginMap:Dictionary = new Dictionary();
    private var _pluginList:ArrayCollection;

    private var notificationFunctions:ArrayCollection;
    private var createdElements:Dictionary;
      // Keys are: MediaElement
      // Values are: Boolean (just a placeholder, the important part is the key)

    private var minimumSupportedFrameworkVersion:String;
    private var dynamicPluginLoader:DynamicPluginLoader;

    private static const STATIC_PLUGIN_MEDIA_INFO_ID:String = "org.osmf.plugins.StaticPluginLoader";
    private static const DYNAMIC_PLUGIN_MEDIA_INFO_ID:String = "org.osmf.plugins.DynamicPluginLoader";
  }
}